﻿using System;
using System.Linq;

using MvcCms.Data.DynamicLINQ;
using MvcCms.Service.Models;
using System.Linq.Expressions;
using System.Collections.Generic;

namespace MvcCms.Service.Code.DataTables
{
    public static class DataTablesSearch
    {
        public static IQueryable SearchForDataTables<T>(this IQueryable<T> query, DataTablesRequest dataTablesRequest, string[] columnNames)
        {
            if (!String.IsNullOrEmpty(dataTablesRequest.sSearch))
            {
                var fieldNames = ParseColumnsForCombinedFields(columnNames);
                #region Search All Fields
                var predicate = PredicateBuilder.False<T>();
                if (dataTablesRequest.sSingleSearchField == "All")
                {
                    Expression<Func<T, bool>> predicateToAdd = null;
                    foreach (var field in fieldNames)
                    {
                        if (dataTablesRequest.bSearchable[field.Key]) // If the column is marked as searchable
                        {
                            string columnName = field.Value;
                            Type columnType = query.DynamicType(z => z[columnName]);
                            if (columnType == typeof(System.Int32))
                            {
                                try
                                {
                                    var intToSearchFor = int.Parse(dataTablesRequest.sSearch);
                                    //for integer we do equals
                                    predicateToAdd = query.DynamicWhereForPredicateBuilder(z => z[columnName] == intToSearchFor);
                                }
                                catch { }
                            }
                            else if (columnType == typeof(System.Decimal))
                            {
                                try
                                {
                                    var decimalToSearchFor = decimal.Parse(dataTablesRequest.sSearch);
                                    //for decimal we do equals
                                    predicateToAdd = query.DynamicWhereForPredicateBuilder(z => z[columnName] == decimalToSearchFor);
                                }
                                catch { }
                            }
                            else if (columnType == typeof(System.DateTime))
                            {
                                try
                                {
                                    var dateToSearchFor = DateTime.Parse(dataTablesRequest.sSearch);
                                    var date2 = dateToSearchFor.AddDays(1.0);
                                    //for DateTime we trap between
                                    predicateToAdd = query.DynamicWhereForPredicateBuilder(z => z[columnName] >= dateToSearchFor.Date
                                        && z[columnName] < date2.Date);
                                }
                                catch { }
                            }
                            else if (columnType == typeof(System.String))
                            {   //for string we do contains, to lower for postgres
                                predicateToAdd = query.DynamicWhereForPredicateBuilder(z => z[columnName].ToLower().Contains(dataTablesRequest.sSearch.ToLower())); //contains or
                            }
                            else if (columnType == typeof(System.Boolean))
                            {
                                try
                                {
                                    var isTrue = bool.Parse(dataTablesRequest.sSearch);
                                    if (isTrue)
                                    {
                                        predicateToAdd = query.DynamicWhereForPredicateBuilder(z => z[columnName]);
                                    }
                                    else
                                    {
                                        predicateToAdd = query.DynamicWhereForPredicateBuilder(z => !z[columnName]);
                                    }
                                }
                                catch { }
                            }
                            if (predicateToAdd != null)
                            {
                                predicate = predicate.Or(predicateToAdd);
                            }
                        }
                    }
                    query = query.Where(predicate);
                }
                #endregion

                #region Search Single Field
                else if (!String.IsNullOrEmpty(dataTablesRequest.sSingleSearchField))
                {
                    string columnName = dataTablesRequest.sSingleSearchField;
                    Type columnType = query.DynamicType(z => z[columnName]);
                    if (columnType == typeof(System.Int32))
                    {
                        try
                        {
                            var intToSearchFor = int.Parse(dataTablesRequest.sSearch);
                            //for integer we do equals
                            query = query.DynamicWhere(z => z[columnName] == intToSearchFor);
                        }
                        catch { }
                    }
                    else if (columnType == typeof(System.Decimal))
                    {
                        try
                        {
                            var decimalToSearchFor = decimal.Parse(dataTablesRequest.sSearch);
                            //for decimal we do equals
                            query = query.DynamicWhere(z => z[columnName] == decimalToSearchFor);
                        }
                        catch { }
                    }
                    else if (columnType == typeof(System.DateTime))
                    {
                        try
                        {
                            var dateToSearchFor = DateTime.Parse(dataTablesRequest.sSearch);
                            var date2 = dateToSearchFor.AddDays(1.0);
                            //for DateTime we trap between
                            query = query.DynamicWhere(z => z[columnName] >= dateToSearchFor.Date
                                && z[columnName] < date2.Date);
                        }
                        catch { }
                    }
                    else if (columnType == typeof(System.String))
                    {
                        //for string we do contains
                        query = query.DynamicWhere(z => z[columnName].Contains(dataTablesRequest.sSearch));
                    }
                    else if (columnType == typeof(System.Boolean))
                    {
                        bool isTrue;
                        try
                        {
                            isTrue = bool.Parse(dataTablesRequest.sSearch);
                            if (isTrue)
                            {
                                query = query.DynamicWhere(z => z[columnName]);
                            }
                            else
                            {
                                query = query.DynamicWhere(z => !z[columnName]);
                            }
                        }
                        catch { }
                    }
                }
                #endregion
            }
            return query;
        }
        private static List<SearchFields> ParseColumnsForCombinedFields(string[] columnNames)
        {
            var gridColumns = columnNames.ToList();
            var fieldsToReturn = new List<SearchFields>();
            var i = 0;
            foreach (var column in gridColumns)
            {
                var combinedFields = StringUtils.ExtractString(column, "[", "]");
                if (!string.IsNullOrEmpty(combinedFields))
                {
                    var combinedFieldsNames = combinedFields.Split('~');
                    foreach (var field in combinedFieldsNames)
                    {
                        //record the index number of the grid column the combined field belongs to
                        var newField = new SearchFields
                        {
                            Key = i,
                            Value = field
                        };
                        fieldsToReturn.Add(newField);
                    }
                }
                else
                {
                    var newField = new SearchFields
                    {
                        Key = i,
                        Value = column
                    };
                    fieldsToReturn.Add(newField);
                }
                i += 1;
            }

            return fieldsToReturn;
        }
    }
    class SearchFields
    {
        public int Key { get; set; }
        public string Value { get; set; }
    }
}


